package br.org.bertol.mestrado.engine.search;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;

import br.org.bertol.mestrado.Verify;
import br.org.bertol.mestrado.engine.Objective;
import br.org.bertol.mestrado.engine.optimisation.entity.Path;
import br.org.bertol.mestrado.engine.optimisation.moo.ParetoDominace;
import br.org.bertol.mestrado.engine.optimisation.moo.ParetoMinMinDominace;

/**
 * @author contaqualquer
 */
public class Greedy<T> {

    /***/
    private transient Verify<T>                            verify;

    /***/
    private final transient ArrayList<Path>             paths;

    /***/
    private final transient boolean                     bestFirst;

    /***/
    private final transient EnumMap<Objective, Integer> emptySet;

    /***/
    private final transient ParetoDominace<EnumMap<Objective, Number>>              paretoMinMinDominace;

    public Greedy(final boolean bestFirst) {
        paths = new ArrayList<Path>();

        this.bestFirst = bestFirst;

        emptySet = new EnumMap<Objective, Integer>(Objective.class);

        for (Objective objective : Objective.values()) {
            emptySet.put(objective, 0);
        }

        paretoMinMinDominace = new ParetoMinMinDominace();
    }

    /**
     * Realiza uma busca gulosa para uma matriz de entrada.
     * @param classes
     *            - Classes de entrada para a busca
     * @return Lista contendo solu��es para as classes de entrada
     * @throws CloneNotSupportedException
     *             Erro ao clonar
     */
    @SuppressWarnings("unchecked")
    public List<Path> greedySearch(final List<T> classes)
            throws CloneNotSupportedException {
        if (paths.size() == 0) {
            for (T c : classes) {
                final Path caminho = new Path(verify.getObjectives());

                // verifica precedencia para a primeira
                // classe do caminho
                caminho.addClass((Integer) c);

                // descarta os caminhos inv�lidos
                if (caminho.calcFitness()) {
                    paths.add(search(caminho, classes));
                }
            }
        }

        return (List<Path>) paths.clone();
    }

    private Path search(final Path caminho, final List<T> classes)
            throws CloneNotSupportedException {
        final List<Path> caminhos = new ArrayList<Path>();

        for (T c : classes) {
            final Path candidato = (Path) caminho.clone();

            if (candidato.addClass((Integer) c) && candidato.calcFitness()) {
                caminhos.add(candidato);
            }
        }

        // verifica qual dos itens tem o menor peso
        Path choosenOne = new Path(verify.getObjectives());

        for (Path postulante : caminhos) {
            if (bestFirst) {
                break;
            }

            // verifica se � melhor
            if (paretoMinMinDominace.compare(postulante.getHeight(), choosenOne
                    .getHeight()) == -1) {
                choosenOne = (Path) postulante.clone();
            }
        }

        // soma peso do caminho at� com o do candidato
        if (caminho.getRoute().size() > 1) {
            choosenOne.sumHeigtht(caminho.getHeight());
        }

        // crit�rio de parada. se percorreu todos as inst�ncias,
        // para tudo e volta solu��o de menor peso
        if (choosenOne.getRoute().size() == classes.size()) {
            return choosenOne;
        }

        // caso contr�rio, continua
        return search(choosenOne, classes);
    }

    /**
     * @param verify
     *            the verify to set
     */
    public final void setVerify(final Verify verify) {
        this.verify = verify;
    }
}
